<template>
  <view class="he-number-box">
    <view
      class="he-icon-minus"
      @touchstart.stop.prevent="btnTouchStart('minus')"
      @touchend.stop.prevent="clearTimer"
      :class="{ 'he-icon-disabled': disabled || inputVal <= min }"
      :style="{
        height: inputHeight + 'rpx'
      }"
    >
      <view class="iconfont iconbtn_subtract"></view>
    </view>
    <input
      :disabled="disabledInput || disabled"
      :cursor-spacing="getCursorSpacing"
      :class="{ 'he-input-disabled': disabled }"
      v-model="inputVal"
      class="he-number-input"
      @blur="onBlur"
      @focus="onFocus"
      type="number"
      :style="{
        height: inputHeight + 'rpx',
        width: inputWidth + 'rpx'
      }"
    />
    <view
      class="he-icon-plus"
      @touchstart.stop.prevent="btnTouchStart('plus')"
      @touchend.stop.prevent="clearTimer"
      :class="{ 'he-icon-disabled': disabled || inputVal >= max }"
      :style="{
        height: inputHeight + 'rpx'
      }"
    >
      <view class="iconfont iconbtn_add"></view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'he-number-box',
  props: {
    // 预显示的数字
    value: {
      type: Number,
      default: 1
    },
    // 最小值
    min: {
      type: Number,
      default: 0
    },
    // 最大值
    max: {
      type: Number,
      default: 99999
    },
    // 步进值，每次加或减的值
    step: {
      type: Number,
      default: 1
    },
    // 是否禁用加减操作
    disabled: {
      type: Boolean,
      default: false
    },
    // input宽度，单位rpx
    inputWidth: {
      type: [Number, String],
      default: 84
    },
    // input高度，单位rpx
    inputHeight: {
      type: [Number, String],
      default: 44
    },
    // index索引，用于列表中使用，让用户知道是哪个numberbox发生了变化，一般使用for循环出来的index值即可
    index: {
      type: [Number, String],
      default: ''
    },
    // 是否禁用输入框，与disabled作用于输入框时，为OR的关系，即想要禁用输入框，又可以加减的话
    // 设置disabled为false，disabledInput为true即可
    disabledInput: {
      type: Boolean,
      default: false
    },
    // 输入框于键盘之间的距离
    cursorSpacing: {
      type: [Number, String],
      default: 100
    },
    // 是否开启长按连续递增或递减
    longPress: {
      type: Boolean,
      default: true
    },
    // 开启长按触发后，每触发一次需要多久
    pressTime: {
      type: [Number, String],
      default: 250
    },
    // 是否只能输入大于或等于0的整数(正整数)
    positiveInteger: {
      type: Boolean,
      default: true
    }
  },
  watch: {
    value(v1, v2) {
      if (!this.changeFromInner) {
        this.inputVal = v1;
        this.$nextTick(function () {
          this.changeFromInner = false;
        });
      }
    },
    inputVal: {
      handler(v1, v2) {
        if (v1 == '') return;
        let value = 0;
        let tmp = this.number(v1);
        if (tmp && v1 >= this.min && v1 <= this.max) value = v1;
        else value = v2;
        if (this.positiveInteger) {
          if (v1 < 0 || String(v1).indexOf('.') !== -1) {
            value = v2;
            this.$nextTick(() => {
              this.inputVal = v2;
            });
          }
        }
        this.handleChange(value, 'change');
      }
    }
  },
  data() {
    return {
      inputVal: 1,
      timer: null,
      changeFromInner: false,
      innerChangeTimer: null
    };
  },
  created() {
    this.inputVal = Number(this.value);
  },
  computed: {
    getCursorSpacing() {
      return Number(uni.upx2px(this.cursorSpacing));
    }
  },
  methods: {
    btnTouchStart(callback) {
      this[callback]();
      if (!this.longPress) return;
      clearInterval(this.timer);
      this.timer = null;
      this.timer = setInterval(() => {
        this[callback]();
      }, this.pressTime);
    },
    clearTimer() {
      this.$nextTick(() => {
        clearInterval(this.timer);
        this.timer = null;
      });
    },
    minus() {
      this.computeVal('minus');
    },
    plus() {
      this.computeVal('plus');
    },
    calcPlus(num1, num2) {
      let baseNum, baseNum1, baseNum2;
      try {
        baseNum1 = num1.toString().split('.')[1].length;
      } catch (e) {
        baseNum1 = 0;
      }
      try {
        baseNum2 = num2.toString().split('.')[1].length;
      } catch (e) {
        baseNum2 = 0;
      }
      baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
      let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2; //精度
      return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision);
    },
    calcMinus(num1, num2) {
      let baseNum, baseNum1, baseNum2;
      try {
        baseNum1 = num1.toString().split('.')[1].length;
      } catch (e) {
        baseNum1 = 0;
      }
      try {
        baseNum2 = num2.toString().split('.')[1].length;
      } catch (e) {
        baseNum2 = 0;
      }
      baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
      let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2;
      return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision);
    },
    computeVal(type) {
      try {
        if (!this.disabledInput) uni.hideKeyboard();
        if (this.disabled) return;
        let value = 0;
        // 减
        if (type === 'minus') {
          value = this.calcMinus(this.inputVal, this.step);
        } else if (type === 'plus') {
          value = this.calcPlus(this.inputVal, this.step);
        }
        value = +value;
        if (value < this.min || value > this.max) {
          return;
        }
        this.inputVal = value;
        this.handleChange(value, type);
      } catch (e) {}
    },
    onBlur(event) {
      let val = 0;
      let value = event.detail.value;
      if (!/(^\d+$)/.test(value) || value[0] == 0) val = this.min;
      val = +value;
      if (val > this.max) {
        val = this.max;
      } else if (val < this.min) {
        val = this.min;
      }
      this.$nextTick(() => {
        this.inputVal = val;
      });
      this.handleChange(val, 'blur');
    },
    onFocus() {
      this.$emit('focus');
    },
    handleChange(value, type) {
      if (this.disabled) return;
      if (this.innerChangeTimer) {
        clearTimeout(this.innerChangeTimer);
        this.innerChangeTimer = null;
      }
      this.changeFromInner = true;
      this.innerChangeTimer = setTimeout(() => {
        this.changeFromInner = false;
      }, 150);
      this.$emit('input', Number(value));
      this.$emit(type, {
        value: Number(value),
        index: this.index
      });
    },
    number(value) {
      return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value);
    }
  }
};
</script>

<style scoped>
.he-number-box {
  display: inline-flex;
  align-items: center;
}

.he-number-input {
  position: relative;
  text-align: center;
  padding: 0;
  margin: 0 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f5f5f5;
  border-radius: 4px;
  font-size: 24px;
  font-family: PingFang SC;
  font-weight: bold;
  color: #222222;
}

.he-icon-plus,
.he-icon-minus {
  width: 44px;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #ffffff;
}

.he-icon-plus {
  border-radius: 0 8px 8px 0;
}

.he-icon-minus {
  border-radius: 8px 0 0 8px;
}

.he-icon-disabled .iconfont {
  color: RGBA(229, 229, 229, 1);
}

.he-input-disabled {
  color: #c8c9cc !important;
  background-color: #f2f3f5 !important;
}

.iconfont {
  width: 20px;
  height: 20px;
  font-size: 20px;
  color: RGBA(102, 102, 102, 1);
}
</style>
